ZFS ZIL(SLOG) 组件
ZIL 虽然用起来有写缓存的效果,但 ZIL 不是写缓存 。
ZIL 全称是 ZFS Intent Log,可以理解成是 ZFS 的写入日志,即使没有添加用于 ZIL 的独立设备,它也存在于 zpool 的内部。它唯一作用就是防止系统崩溃时丢失正在写入的数据: 数据会先被写入 ZIL 再被写入物理硬盘,写两遍。这就意味着,如果不出现系统突然挂掉之类的情况,ZIL 中的数据基本没用。
ZFS 的写入操作在 sync=always 的情况下,到达内存的数据必须先作为 ZIL 数据被写入到硬盘上,然后写操作才能返回成功。如果服务器挂掉,那么系统重启之后,ZFS 可以根据硬盘上的 ZIL 数据找到没有来得及写入 ZFS 的数据,重新写入 ZFS 文件系统,不会丢数据。
但如果是 sync=disabled 那么数据到达内存后,写操作就可以返回成功了,后台进程会在一段时间 (5s) 之后把内存中的数据写到物理硬盘上 (先向物理硬盘的 ZIL 写一次,再向文件系统写一次),所以如果这 5s 收到的数据如果还没来得及全部写到硬盘里机器就挂了,那么没来得及写入的数据就永久丢失了。
考虑到绝大多数情况下 zpool 都建立在机械硬盘上,所以 ZIL 也被放到机械硬盘组成的 zpool 里,但是 ZIL 写入操作全是 4K 写操作 ,而组成 zpool 的机械硬盘 4K 写性能太差了,所以 ZFS 搞出了一个 SLOG (Separate intent LOG) 组件, SLOG 组件必须是一个独立设备或分区,专门存放 ZIL 数据 。
后文用 SLOG 设备 指代 具有 ZIL 功能的独立设备或分区 。
根据 Oracle Solaris 关于 ZFS and Cache Flushing 的文档,在 uberblock 更新后,ZFS 大约每隔 5s 将缓存中所有未写入的数据写入到硬盘上 (每次程序请求同步写入,比如 O_DSYNC, fsync, NFS commit 等操作时,ZFS 也会刷新数据)。这里的将缓存写入硬盘是指 ZFS 将内存中保存的、已经在 ZIL 记录过的数据,写入到物理硬盘上。
注: uberblock 记录了整个 ZFS Pool 的信息,Pool 中的每个硬盘上都有 4 个 uberblock 的 copy。它的作用与 UFS 中的 superblock 类似。由于它记录了 ZFS Pool 的信息,所以当 ZFS 被更新时,它也会被更新。从代码结构上讲,它更像是一个指向 ZFS 树根节点的指针。
5s 这个数据意味着,当服务器作为 NAS 使用,ZIL 数据的规模并不大 (没必要用大容量 SSD):
- 双千兆网卡满速传输时 ZIL 数据最多有 1000 Mbit/s * 5s * 2 = 1 Gbit = 10^10 / 1024^2 / 8 MiB = 1192.0929 MiB < 1.17 GiB
- 双万兆网卡满速传输时 ZIL 数据最多有 (10*1000) Mbit/s * 5s * 2 = 10 Gbit = 10^11 / 1024^2 / 8 MiB = 11920.929 MiB < 11.7 GiB
注: 1 G = 1000 M, 1 M = 1000 K, 1 Gi = 1024 Mi, 1 Mi = 1024 Ki, 1 Byte = 8 bit
ZFS 收到数据之后:
- 先将数据写在内存中
- 再把内存中的数据写一份到 ZIL 中
- 最后把保存在内存中的、已被写入到 ZIL 的数据再写一份到 ZFS 文件系统 (写入物理硬盘)
数据在真正被写入文件系统 (物理硬盘) 之前共有两份,一份在内存中,另一份在 ZIL 中。
数据被处理的整个过程中会有三份,内存中、ZIL 中和文件系统 (物理硬盘) 中。
整个数据处理过程中,写硬盘的操作 串行 发生了两次,第一次是写 ZIL 数据 (4K 写),第二次的写操作是把数据存入硬盘上的文件系统 (4K 或顺序写)。
发生在 ZIL 的 4K 写操作就是性能下降的原因。
所以只要让所有的 ZIL 写操作都发生在 4K 性能好的 SSD 上,整个 zpool 的性能就上去了。这时候 SLOG 就派上了用场。
根据文档,SLOG 设备必须是一个独立设备,如果这个设备的 4K 同步写性能很好的话可以提升整体的 zpool 的同步写性能。所以之前会用 ZeusRAM 现在会用 Optane 来存储 ZIL 数据,因为这种设备写入能力很强。
给一个实机测试:
- 主机到 NAS 的最大速度为千兆
- 在傲腾 M10 16G 硬盘上,为每个 zpool 分 1G 的分区作为 SLOG 设备
- mirror1 由 2 个 8T SATA 企业级 HDD 组成
- mirror2 由 2 个 3T SAS HDD 组成
- 使用 NFSv3 协议共享这些 zpool
NFSv3 客户端下,写入 100G 文件的稳定速度可以由原来的 15 - 18 MB/s 提升到 45 - 50 MB/S (接近傲腾 M10 的 4K 写速率),性能瓶颈在傲腾的 4K 写入上。如果 SLOG 设备 4K 写性能更强,写入速度会更快。
作为 NAS 使用,单个 zpool 分 2 倍理论数据的规模已经足够。即,使用双万兆网卡负载均衡就给每个 zpool 分 23.4 GiB 大小的 SLOG 设备;使用双千兆网卡负载均衡就给每个 zpool 分 2.34 GiB 大小的 SLOG 设备。
注: FreeNAS 文档里提到,它们推荐的 SLOG 设备大小是 16 GiB。
如果用的是 905p/900p,那么在分出用做 SLOG 的空间后,剩下的可以都拿去做 L2ARC。千兆环境用 16GB 傲腾,万兆用 58GB 的 800p 已经足够做 SLOG 了。
除了每 5s 将缓存数据写入硬盘之外,当缓存的数据的大小达到 sysctl vfs.zfs.dirty_data_max_max
所指定的值时,也会触发写入事务。所以 SLOG 设备容量的最大值不应超过 vfs.zfs.dirty_data_max_max 定义的大小 。
因为当未被写入硬盘的数据的大小超过 vfs.zfs.dirty_data_max_max 时,ZFS 立即触发硬盘写入,每条数据被写入硬盘后,该条数据占用的 ZIL 空间会被释放。所以超出 vfs.zfs.dirty_data_max_max 大小的空间不会被 SLOG 使用,多余的空间没有意义。